<template>
  <el-container style="width: 100%;height: 100%;">
    <!--    <el-container style="width: 100%;height: 100%;">-->
    <el-main
      style="height: 100%;margin-right: 10px;border-color: #E4E7ED;border-width: 10px"
    >
      <el-form>
        <el-row style="height: 100%">
          <el-col style="width: 320px">
            <el-form-item label="原始数据类型:">
              <div style="text-align: left">
                <!--              el-select默认居中显示，外面加一层div控制位置-->
                <el-select v-model="srcDataType" placeholder="请选择">
                  <el-option
                    v-for="item in srcDataTypeList"
                    :key="item.idx"
                    :label="item.value"
                    :value="item.value"
                  ></el-option>
                </el-select>
              </div>
            </el-form-item>
          </el-col>
          <el-col style="width: 300px">
            <el-form-item label="字符类型:">
              <div style="text-align: left">
                <!--              el-select默认居中显示，外面加一层div控制位置-->
                <el-select v-model="charDataType" placeholder="请选择">
                  <el-option
                    v-for="item in charDataTypeList"
                    :key="item.idx"
                    :label="item.value"
                    :value="item.value"
                  ></el-option>
                </el-select>
              </div>
            </el-form-item>
          </el-col>
          <el-input
            type="textarea"
            :rows="10"
            placeholder="请输入内容"
            v-model="srcTextarea"
            style="margin-top: 5px"
          >
          </el-input>
        </el-row>
      </el-form>

      <el-form style="margin-top: 10px">
        <el-form-item label="结果数据类型:">
          <div style="text-align: left">
            <!--              el-select默认居中显示，外面加一层div控制位置-->
            <el-select v-model="dstDataType" placeholder="请选择">
              <el-option
                v-for="item in dstDataTypeList"
                :key="item.idx"
                :label="item.value"
                :value="item.value"
              ></el-option>
            </el-select>
            <el-button
              style="margin-left: 5px"
              v-on:click="fLowerCase"
              v-show="showLowerCaseBtn"
              >lowercaseString</el-button
            >
          </div>
          <el-input
            type="textarea"
            :rows="10"
            placeholder="显示结果"
            v-model="dstTextarea"
            style="margin-top: 5px"
          >
          </el-input>
        </el-form-item>
      </el-form>
      <el-row>
        <el-button type="primary" style="width: 300px" v-on:click="fHash"
          >执行Hash计算</el-button
        >
        <el-button type="primary" style="width: 300px" v-on:click="fHmac"
          >hmac计算</el-button
        >
      </el-row>
    </el-main>

    <el-aside style="width: 360px">
      <el-form style="margin-left: 15px">
        <el-form-item style="color: chocolate">配置参数</el-form-item>
        <el-form-item label="hash类型:">
          <el-select v-model="hashDataType" placeholder="请选择">
            <el-option
              v-for="item in hashDataTypeList"
              :key="item.idx"
              :label="item.value"
              :value="item.value"
            ></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="hmac key">
          <el-select v-model="hmacDataType" placeholder="请选择">
            <el-option
              v-for="item in hmacDataTypeList"
              :key="item.idx"
              :label="item.value"
              :value="item.value"
            ></el-option>
          </el-select>
          <el-input
            type="textarea"
            :rows="2"
            placeholder="密钥长度和分组长度一致"
            v-model="hmacKey"
            style="margin-top: 5px"
          >
          </el-input>
        </el-form-item>
        <el-form-item
          label="md5:摘要长度16字节(128位)分组长度64字节"
        ></el-form-item>
        <el-form-item
          label="SHA1:摘要长度 20字节(160位) 分组长度64字节"
        ></el-form-item>
        <el-form-item
          label="SHA256:摘要长度 32字节(256位) 分组长度64字节"
        ></el-form-item>
        <el-form-item
          label="SHA512:摘要长度 64字节(512位) 分组长度128字节"
        ></el-form-item>
        <el-form-item
          label="SM3:摘要长度 32字节(128位) 分组长度64字节"
        ></el-form-item>
        <el-form-item
          label="hmac密钥需要与分组长度一致，实际不足补0"
        ></el-form-item>
      </el-form>
    </el-aside>
  </el-container>
</template>

<script>
import Crypto from "crypto-js";
import sm3 from "../JS/sm3";

export default {
  name: "Hash",
  data() {
    return {
      srcDataTypeList: [
        { value: "plain", idx: "1" },
        { value: "hex", idx: "2" },
        { value: "base64", idx: "3" }
      ],
      srcDataType: "plain",
      srcTextarea: "",

      dstDataTypeList: [
        { value: "hex", idx: "1" },
        { value: "base64", idx: "2" }
      ],
      dstDataType: "hex",
      dstTextarea: "",

      // type BufferEncoding = "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex";
      charDataTypeList: [
        { value: "utf8", idx: "1" },
        { value: "ascii", idx: "2" },
        { value: "latin1", idx: "3" },
        { value: "ucs2", idx: "4" }
      ],
      charDataType: "utf8",

      hashDataTypeList: [
        { value: "MD5", idx: "1" },
        { value: "SHA1", idx: "2" },
        { value: "SHA2-256", idx: "3" },
        { value: "SHA2-512", idx: "4" },
        { value: "SM3", idx: "5" }
      ],
      hashDataType: "MD5",

      //hmac
      hmacDataTypeList: [
        { value: "plain", idx: "1" },
        { value: "hex", idx: "2" },
        { value: "base64", idx: "3" }
      ],
      hmacDataType: "plain",
      hmacKey: ""
    };
  },
  computed: {
    showLowerCaseBtn: function() {
      return this.dstDataType == "hex" ? true : false;
    }
  },
  methods: {
    // md5 摘要长度 16字节(128位)  分组长度64字节
    // SHA1 摘要长度 20字节(160位) 分组长度64字节
    // SHA256 摘要长度 32字节(256位) 分组长度64字节
    // SHA512 摘要长度 64字节(512位) 分组长度128字节
    // SM3 摘要长度 32字节(128位) 分组长度64字节
    // SM3强度与SHA256 一个级别
    // hmac密钥需要与分组长度一致，实际不足补0

    fHmac: function() {
      if (this.hashDataType == "SM3") {
        this.fhmacSM3();
        return;
      }

      let hmac = Crypto.HmacMD5;

      switch (this.hashDataType) {
        case "MD5":
          hmac = Crypto.HmacMD5;
          break;
        case "SHA1":
          hmac = Crypto.HmacSHA1;
          break;
        case "SHA2-256":
          hmac = Crypto.HmacSHA256;
          break;
        case "SHA2-512":
          hmac = Crypto.HmacSHA512;
          break;
      }

      let wordArray;
      switch (this.srcDataType) {
        case "plain":
          wordArray = Crypto.enc.Utf8.parse(this.srcTextarea);
          break;
        case "hex":
          wordArray = Crypto.enc.Hex.parse(this.srcTextarea);
          break;
        case "base64":
          wordArray = Crypto.enc.Base64.parse(this.srcTextarea);
          break;
      }

      let key;
      switch (this.hmacDataType) {
        case "plain":
          key = Crypto.enc.Utf8.parse(this.hmacKey);
          break;
        case "hex":
          key = Crypto.enc.Hex.parse(this.hmacKey);
          break;
        case "base64":
          key = Crypto.enc.Base64.parse(this.hmacKey);
          break;
      }

      let result = hmac(wordArray, key);

      if (this.dstDataType == "base64") {
        this.dstTextarea = result.toString(Crypto.enc.Base64);
      } else {
        //默认输出hex字符串
        this.dstTextarea = result.toString().toUpperCase();
      }
    },
    fhmacSM3: function() {
      let blocksize = 64;

      let wordArray;
      switch (this.srcDataType) {
        case "plain":
          wordArray = Crypto.enc.Utf8.parse(this.srcTextarea);
          break;
        case "hex":
          wordArray = Crypto.enc.Hex.parse(this.srcTextarea);
          break;
        case "base64":
          wordArray = Crypto.enc.Base64.parse(this.srcTextarea);
          break;
      }

      let arrayU8Src = this.CryptJsWordArrayToUint8Array(wordArray);

      switch (this.hmacDataType) {
        case "plain":
          wordArray = Crypto.enc.Utf8.parse(this.hmacKey);
          break;
        case "hex":
          wordArray = Crypto.enc.Hex.parse(this.hmacKey);
          break;
        case "base64":
          wordArray = Crypto.enc.Base64.parse(this.hmacKey);
          break;
      }

      let arrayU8Key = this.CryptJsWordArrayToUint8Array(wordArray);

      if (arrayU8Key.length != blocksize) {
        let tmp = new Uint8Array(blocksize);
        if (arrayU8Key.length < blocksize) {
          tmp.set(arrayU8Key);
          for (
            let i = arrayU8Key.length;
            i < blocksize - arrayU8Key.length;
            i++
          ) {
            tmp[i] = 0x00;
          }
        } else {
          for (let i = 0; i < blocksize; i++) {
            tmp[i] = arrayU8Key[i];
          }
        }
        arrayU8Key = tmp;
      }

      let o_key_pad = new Uint8Array(blocksize);
      let i_key_pad = new Uint8Array(blocksize);
      for (let i = 0; i < blocksize; i++) {
        o_key_pad[i] = 0x5c ^ arrayU8Key[i];
        i_key_pad[i] = 0x36 ^ arrayU8Key[i];
      }

      let rightHex1 = Buffer.from(i_key_pad).toString("hex");

      let rightHex2 = Buffer.from(arrayU8Src).toString("hex");

      let rightHex = rightHex1 + rightHex2;
      console.log("rightHex:" + rightHex);

      //hex
      let rightHexSM3 = sm3(rightHex);
      console.log("CryptJsWordArrayToUint8Array");

      console.log(rightHexSM3);

      let lastHex1 = Buffer.from(o_key_pad).toString("hex");
      let lastHex = lastHex1 + rightHexSM3;

      let lastSM3Hex = sm3(lastHex);

      console.log(lastSM3Hex);

      if (this.dstDataType == "base64") {
        let wordArray = Crypto.enc.Hex.parse(lastSM3Hex);

        this.dstTextarea = Crypto.enc.Base64.stringify(wordArray);
      } else {
        //默认输出hex字符串
        this.dstTextarea = lastSM3Hex.toUpperCase();
      }
    },
    fHash: function() {
      console.log(this.srcTextarea);

      if (this.hashDataType == "SM3") {
        this.fHashSM3();
        return;
      }

      let hash = Crypto.MD5;

      switch (this.hashDataType) {
        case "MD5":
          hash = Crypto.MD5;
          break;
        case "SHA1":
          hash = Crypto.SHA1;
          break;
        case "SHA2-256":
          hash = Crypto.SHA256;
          break;
        case "SHA2-512":
          hash = Crypto.SHA512;
          break;
      }

      let wordArray;
      switch (this.srcDataType) {
        case "plain":
          // result = hash(this.srcTextarea);
          wordArray = Crypto.enc.Utf8.parse(this.srcTextarea);
          break;
        case "hex":
          wordArray = Crypto.enc.Hex.parse(this.srcTextarea);
          console.log(wordArray);
          break;
        case "base64":
          wordArray = Crypto.enc.Base64.parse(this.srcTextarea);
          break;
      }
      let result = hash(wordArray);

      if (this.dstDataType == "base64") {
        this.dstTextarea = result.toString(Crypto.enc.Base64);
      } else {
        //默认输出hex字符串
        this.dstTextarea = result.toString().toUpperCase();
      }
    },
    fHashSM3: function() {
      let hex;
      switch (this.srcDataType) {
        case "plain":
          // result = hash(this.srcTextarea);
          hex = Crypto.enc.Utf8.parse(this.srcTextarea).toString(
            Crypto.enc.Hex
          );
          break;
        case "hex":
          hex = this.srcTextarea;
          break;
        case "base64":
          hex = Crypto.enc.Base64.parse(this.srcTextarea).toString(
            Crypto.enc.Hex
          );
          break;
      }

      let result = sm3(hex);
      if (this.dstDataType == "base64") {
        result = Crypto.enc.Hex.parse(result);
        result = Crypto.enc.Base64.stringify(result);
        this.dstTextarea = result;
      } else {
        //默认输出hex字符串
        this.dstTextarea = result;
      }
    },
    fLowerCase: function() {
      this.dstTextarea = this.dstTextarea.toLowerCase();

      var plainUtf8 = "1111";

      let plainBuffer = Buffer.from(plainUtf8, "utf-8");

      //-> hex
      let hex = plainBuffer.toString("hex");
      console.log("hex:" + hex);

      var wordArray = Crypto.enc.Hex.parse(hex);
      console.log(wordArray);

      // Encrypt
      let ciphertext = Crypto.AES.encrypt(
        wordArray,
        "secret key 123"
      ).toString();
      //base64
      console.log("encrypt base64:" + ciphertext);

      // Decrypt
      let bytes = Crypto.AES.decrypt(ciphertext, "secret key 123");
      // var decryptedData = JSON.parse(bytes.toString(Crypto.enc.Utf8));

      console.log(bytes); // [{id: 1}, {id: 2}]

      //hex
      console.log(bytes.toString());

      console.log(Buffer.from(bytes.toString(), "hex").toString("utf8"));

      console.log("above plain");

      //->base64
      let base64 = plainBuffer.toString("base64");
      console.log("base64:" + base64);

      //->string utf-8
      let utf8 = plainBuffer.toString("utf-8");
      console.log("utf8:" + utf8);

      //byte数组
      console.log(typeof plainBuffer.buffer);
      console.log(plainBuffer.buffer);

      console.log("hex to buf:");
      console.log(Buffer.from(hex, "hex"));

      let str1 = Buffer.from(hex, "hex").toString("utf-8");
      console.log("hex to utf-8 string:" + str1);

      //json与string相互转换
      this.json2String();

      //test encrpto
      this.testEncrypto();
    },

    // hexToString: function(hex) {
    //   var arr = hex.split("");
    //   var out = "";
    //
    //   for (var i = 0; i < arr.length / 2; i++) {
    //     var tmp = "0x" + arr[i * 2] + arr[i * 2 + 1];
    //     var charValue = String.fromCharCode(tmp);
    //     out += charValue;
    //   }
    //   return out;
    // },

    /* Converts a cryptjs WordArray to native Uint8Array */
    CryptJsWordArrayToUint8Array: function(wordArray) {
      const l = wordArray.sigBytes;
      const words = wordArray.words;
      const result = new Uint8Array(l);
      var i = 0 /*dst*/,
        j = 0; /*src*/
      while (i != l) {
        // here i is a multiple of 4
        if (i == l) break;
        var w = words[j++];
        result[i++] = (w & 0xff000000) >>> 24;
        if (i == l) break;
        result[i++] = (w & 0x00ff0000) >>> 16;
        if (i == l) break;
        result[i++] = (w & 0x0000ff00) >>> 8;
        if (i == l) break;
        result[i++] = w & 0x000000ff;
      }
      return result;
    },

    json2String: function() {
      //json,注意手动拼写格式
      let json = '{ "name": "cxh", "sex": "man" }';
      // let json  = { name: "cxh", sex: "man" };

      if (typeof json == "string") {
        //string->json
        console.log("string object");
        let str;
        try {
          str = JSON.parse(json);
          console.log("string->json" + str);
        } catch (e) {
          console.log("err:" + str);
        }
      } else {
        //json -> string

        let jsonobj = JSON.stringify(json);

        console.log("json->string:" + jsonobj + typeof jsonobj);
      }
    },
    testEncrypto: function() {
      console.log("test encrypto");

      let data = [{ id: 1 }, { id: 2 }];

      // Encrypt
      let ciphertext = Crypto.AES.encrypt(
        JSON.stringify(data),
        "secret key 123"
      ).toString();
      //base64
      console.log(ciphertext);

      // Decrypt
      let bytes = Crypto.AES.decrypt(ciphertext, "secret key 123");
      // var decryptedData = JSON.parse(bytes.toString(Crypto.enc.Utf8));

      console.log(bytes); // [{id: 1}, {id: 2}]

      //hex
      console.log(bytes.toString());

      console.log(Buffer.from(bytes.toString(), "hex").toString("utf8"));
    }
  },

  created() {
    // 方便控制台获取当前vue进行数据调试
    window.Hash = this;
  }
};
</script>

<style scoped>
.el-main {
  background-color: #e9eef3;
  color: #333;
  text-align: center;
  line-height: 50px;
}
.left {
  text-align: left;
}

.el-aside {
  display: block;
  position: relative;
  /*overflow-y: scroll;*/
  /*background-color: #324157 !important;*/
  width: 440px;
  border-left-color: #c8c8c8;
  border-left-style: solid;
  border-left-width: 1px;
}
</style>
